//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created 6/9/2008 10:20:47 AM
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.Collections;
using MapWindow.Drawing;
using MapWindow.Geometries;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MapWindow.DirectX
{


    /// <summary>
    /// VertexCollection
    /// </summary>
    public abstract class VertexCollection
    {
        #region Events

        /// <summary>
        /// Occurs when the Vertex Collection has been completely initialized during
        /// a drawing cycle.
        /// </summary>
        public event EventHandler Initialized;

        /// <summary>
        /// Occurs when the Vertex Collection is invalidated
        /// </summary>
        public event EventHandler Invalidated;

        #endregion

        #region Private Variables

        private Device _device;
        private bool _isInitialized;
        private VertexFormats _format;
        private VertexBuffer _vertexBuffer;
        private ArrayList _vertices;

        private int _startVertex = 0;
        private int _count = -1;
        private DrawBox _drawBox;

        #endregion

        #region Constructors

       

        #endregion

        #region Methods

        /// <summary>
        /// Adds a coordinate
        /// </summary>
        /// <param name="coord"></param>
        public virtual void AddCoordinate(Coordinate coord)
        {
            // adds the coordinate.  This needs to be overridden 
        }

        /// <summary>
        /// Adds a new coordinate to the vertex collection
        /// </summary>
        /// <param name="coord"></param>
        public virtual void AddCoordinate(Vector3 coord)
        {

        }

        /// <summary>
        /// This simply assigns the vertex buffer as the stream for the specified device.
        /// If this collection has not yet been initialized, it will use the coordinates
        /// at this time to build a new vertex buffer.
        /// </summary>
        /// <param name="device"></param>
        public virtual void BeforeDrawing(Device device)
        {
            _device = device;
            
            //_device.VertexFormat = Format;
            if (_isInitialized == false)
            {
                Initialize();
            }
            if (Format == CustomVertex.PositionColored.Format)
            {
                _device.VertexFormat = CustomVertex.PositionColored.Format;
            }
            else
            {
                _device.VertexFormat = CustomVertex.PositionTextured.Format;
                
            }
            _device.SetStreamSource(0, _vertexBuffer, 0);
        }

        /// <summary>
        /// This should be called after drawing, allowing the Vertexcollection to 
        /// reset the stream.  This way memory is freed.
        /// </summary>
        /// <param name="device"></param>
        public virtual void AfterDrawing(Device device)
        {
            device.SetStreamSource(0, null, 0);
        }

        /// <summary>
        /// This disposes the vertexBuffer, but has no affect on the managed variables.
        /// </summary>
        public virtual void Dispose()
        {
            if(_vertexBuffer != null)_vertexBuffer.Dispose();
        }

        /// <summary>
        /// Invalidates the given buffer so that the vertex buffer will be re-calculated during 
        /// the next drawing cycle.
        /// </summary>
        public virtual void Invalidate()
        {
            IsInitialized = false;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        public virtual void Validate(DrawBox db)
        {
            if (_drawBox == db) return;
            _drawBox = db;
            Invalidate();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the count of the current vertices in the collection.
        /// This looks at the managed vertices, not the buffer.
        /// </summary>
        public int Count
        {
            get { return _vertices.Count; }
        }


        /// <summary>
        /// Gets or sets the StartVertex
        /// </summary>
        public int StartVertex
        {
            get
            {
                return _startVertex;
            }
            set
            {
                if (_startVertex < 0)
                {
                    _startVertex = 0;
                    return;
                }
                _startVertex = value;
            }
        }

        /// <summary>
        /// Gets or sets the number of vertices to draw.  This defaults
        /// to whatever the vertex count is, but can be set to a value
        /// less than the count.  
        /// </summary>
        public int NumVerticesToDraw
        {
            get
            {
                if (_count == -1) return _vertices.Count;
                return _count;
            }
            set
            {
                if (value < -1)
                {
                    throw new ArgumentOutOfRangeException("Cannot specify a value less than -1.");
                }
                if (value > _vertices.Count)
                {
                    throw new ArgumentOutOfRangeException("Cannot specify a value greater than count.");
                }
                if (_startVertex + value > _vertices.Count)
                {
                    throw new ArgumentOutOfRangeException("With the specified start index, this value will exceed the vertex count.");
                }
                _count = value;
                
            }
        }

        /// <summary>
        /// Gets or sets the device
        /// </summary>
        public Device DeviceDX
        {
            get { return _device; }
            set { _device = value; }
        }

        /// <summary>
        /// Gets or sets the draw window
        /// </summary>
        public DrawBox DrawBox
        {
            get { return _drawBox; }
            set { _drawBox = value; }
        }

        /// <summary>
        /// Gets the format used when creating the vertices.
        /// </summary>
        public VertexFormats Format
        {
            get { return _format; }
            protected set { _format = value; }
        }

        /// <summary>
        /// Gets whether or not the buffer itself has been initialized
        /// </summary>
        public bool IsInitialized
        {
            get { return _isInitialized; }
            protected set { _isInitialized = value; }
        }

        /// <summary>
        /// Gets the vertex buffer
        /// </summary>
        public VertexBuffer VertexBuffer
        {
            get { return _vertexBuffer; }
            protected set { _vertexBuffer = value; }
        }

      
        /// <summary>
        /// Gets or sets the vertices.
        /// </summary>
        public ArrayList Vertices
        {
            get { return _vertices; }
            set { _vertices = value; }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Assigns the values to a new vertex buffer
        /// </summary>
        protected virtual void Initialize()
        {
            _vertexBuffer.Created += new EventHandler(_vertexBuffer_Created);
            OnInitialized(_vertexBuffer, new EventArgs());
            // This should be overridden in subclasses
        }

       

       
        void _vertexBuffer_Created(object sender, EventArgs e)
        {
            OnInitialized(this, new EventArgs());
        }

        /// <summary>
        /// Raises the Initialized event
        /// </summary>
        /// <param name="sender">The object sender</param>
        /// <param name="e">The Event Args</param>
        protected virtual void OnInitialized(object sender, EventArgs e)
        {
            if (Format == CustomVertex.PositionTextured.Format)
            {
                CustomVertex.PositionTextured[] vals = new CustomVertex.PositionTextured[_vertices.Count];
                _vertices.CopyTo(0, vals, 0, _vertices.Count);
                bool success = false;
                while (success == false)
                {
                    try
                    {
                        _vertexBuffer.SetData(vals, 0, LockFlags.None);
                        success = true;
                    }
                    catch
                    {
                        success = false;
                    }
                }
            }
            else if (Format == CustomVertex.PositionColored.Format)
            {
                CustomVertex.PositionColored[] vals = new CustomVertex.PositionColored[_vertices.Count];
                _vertices.CopyTo(0, vals, 0, _vertices.Count);
                _vertexBuffer.SetData(vals, 0, LockFlags.None);
            }
            
           
            if (Initialized != null)
            {
                Initialized(sender, e);
            }
            _isInitialized = true;
        }

        /// <summary>
        /// Raises the Invalidated method using the sender and EventArgs specified.
        /// </summary>
        /// <param name="sender">The object sender</param>
        /// <param name="e">The System.EventArgs</param>
        protected virtual void OnInvalidated(object sender, EventArgs e)
        {
            if (Invalidated != null)
            {
                Invalidated(sender, e);
            }
        }


        #endregion

    }
}
